/* Software Name : AsmDex
 * Version : 1.0
 *
 * Copyright © 2012 France Télécom
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holders nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.ow2.asmdex.structureWriter;

import org.ow2.asmdex.Opcodes;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;

/**
 * Constant Pool of the Application. It has a link to every element the Application holds.
 * 
 * Dalvik requires some structures to be sorted. It is faster to sort them once the Application is parsed
 * than adding elements one after one, so we manage first simple collections, and then the
 * prepareGeneration method builds the sorted ones.
 * 
 * Also, we use Symbolic elements from Strings, Fields, Types and Methods so that we can generate the bytecode
 * right after each method is parsed. This is useful to be able to destroy the elements related the Methods
 * when their parsing is over. The indexes are then remapped correctly after the Application is fully parsed
 * (see {@link CodeItem#mapResolvedIndexes()}).
 *
 * NOTES :
 * - Empty Strings can be added to the Constant Pool, but only if useful. Empty parameters aren't encoded
 *   in the Constant Pool (yet they are declared in the Debug Item). This is tested by the Debug Item
 *   encoder though, not the Constant Pool itself.
 *   
 * - a "this" String is added by dx, I don't know exactly where it comes from. I add it when a
 *   non-static/non-constructor Method is found, it seems logical.
 *  
 * @author Julien Névo
 */
public class ConstantPool {

	/**
	 * Indicates whether the searches must be performed on Symbolic elements (where available) or not.
	 * It matters only for Strings, Fields, Types and Methods.
	 */
	private boolean useSymbolicElements = true;
	
	/* Here follows the symbolic maps. They are useful to instructions, because the opcode of methods
	 * is generated at the end of each of them, and the list of instructions are erased in order to
	 * reduce memory consumption. But indexes to Strings, Fields, Types and Methods, which are used by
	 * instructions, are only definitely known when the whole application has been parsed, as they must
	 * be ordered within the Dex file.
	 * The solution consists in using symbolic Strings/Fields/Types/Methods which refers an index
	 * that will later be mapped into the "real" index, once the full application is parsed.
	 */
	/**
	 * Map linking a symbolic String to its index.
	 */
	private HashMap<String, Integer> symbolicStringsToIndexes = new HashMap<String, Integer>();

	/**
	 * Map linking a symbolic Types to its index.
	 */
	private HashMap<String, Integer> symbolicTypesToIndexes = new HashMap<String, Integer>();

	/**
	 * Map linking a symbolic Methods to its index.
	 */
	private HashMap<Method, Integer> symbolicMethodsToIndexes = new HashMap<Method, Integer>();

	/**
	 * Map linking a symbolic Fields to its index.
	 */
	private HashMap<Field, Integer> symbolicFieldsToIndexes = new HashMap<Field, Integer>();

	
	
	/**
	 * Array linking a symbolic String index to a resolved String index.
	 */
	private int[] symbolicStringsIndexToResolvedStringsIndex;
	
	/**
	 * Array linking a symbolic Field index to a resolved Field index.
	 */
	private int[] symbolicFieldsIndexToResolvedFieldsIndex;
	
	/**
	 * Array linking a symbolic Field index to a resolved Field index.
	 */
	private int[] symbolicMethodsIndexToResolvedMethodsIndex;
	
	/**
	 * Array linking a symbolic Field index to a resolved Field index.
	 */
	private int[] symbolicTypesIndexToResolvedTypesIndex;
		
	/*
	 * The following maps links the structures to an index. It is only filled once the application has been
	 * fully parsed.
	 */
	private HashMap<String, Integer> resolvedStringsToIndexes = new HashMap<String, Integer>();
	private HashMap<String, Integer> resolvedTypesToIndexes = new HashMap<String, Integer>();
	private HashMap<Method, Integer> resolvedMethodsToIndexes = new HashMap<Method, Integer>();
	private HashMap<Field, Integer> resolvedFieldsToIndexes = new HashMap<Field, Integer>();
	
	private HashMap<Prototype, Integer> prototypesToIndexes = new HashMap<Prototype, Integer>();
	//private HashMap<ClassDefinitionItem, Integer> classesToIndexes = new HashMap<ClassDefinitionItem, Integer>();
	
	/**
	 * Map linking a TypeList structure to its offset from the beginning of the file.
	 */
	private HashMap<TypeList, Integer> typeListsToOffsets = new HashMap<TypeList, Integer>();
	
	/**
	 * Ordered Set in which the newly discovered Strings are added. This is useful to order the
	 * Strings when assigning them indexes, as requested by the Dex format. 
	 */
	private TreeSet<String> strings = new TreeSet<String>();
	
	/**
	 * Unordered Set in which the newly discovered prototypes are added. The Prototypes will be sorted
	 * only when the Application has been fully parsed. The ordered collection is just below.
	 */
	private HashMap<Prototype,Prototype> prototypes = new HashMap<Prototype,Prototype>();
	
	/**
	 * List of Ordered Prototypes. It is only completed when everything has been parsed.
	 */
	private TreeSet<Prototype> orderedPrototypes;
	
	/**
	 * Ordered Set in which the newly discovered Types are added. A Type added here must also
	 * be added in the strings structure above.
	 */
	private TreeSet<String> types = new TreeSet<String>();
	
	/**
	 * Ordered Set in which the newly discovered Fields are added. The Fields are sorted by the 
	 * class owning name, their own name, and their Type. 
	 */
	private HashMap<Field,Field> fields = new HashMap<Field,Field>();
	
	/**
	 * Unordered Set containing Methods. They will be sorted when all the Application is parsed.
	 * This is done to speed up the addition of new Methods. The ordered collection is just below.
	 */
	private HashMap<Method,Method> methods = new HashMap<Method,Method>();
	
	/**
	 * List of Ordered Fields. It is only completed when everything has been parsed.
	 */
	private TreeSet<Field> orderedFields;
	
	/**
	 * List of Ordered Methods. It is only completed when everything has been parsed.
	 */
	private TreeSet<Method> orderedMethods;

	/**
	 * List of Ordered Classes. It is only completed when everything has been parsed.
	 */
	private ArrayList<ClassDefinitionItem> orderedClasses;
	
	/**
	 * Unordered Set containing TypeList, from to the type_list structure.
	 * The ordered list is below, constructed after all has been parsed.
	 */
	private HashSet<TypeList> typeList = new HashSet<TypeList>();
	
	/**
	 * Set of Ordered TypeLists. It is only completed when everything has been parsed.
	 */
	private TreeSet<TypeList> orderedTypeList;
	
	/**
	 * Map linking a String from the Type_ids list (as encoded in the future Dex file) to the
	 * String index in the string_ids. This map is created only when the header is being built. 
	 */
	//private HashMap<String, Integer> stringsToTypeIdIndexes = new HashMap<String, Integer>();

	/**
	 * Map linking a ClassDefinitionItem to its static_value_encoded_array_item offset. Only Classes
	 * with an actual array are encoded. If a Class doesn't found its array, it means it has none.
	 * This map is created when the value_encoded_array_items are built.
	 */
	private HashMap<ClassDefinitionItem, Integer> staticValuesEncodedArrayItemOffsets = new HashMap<ClassDefinitionItem, Integer>();
	
	
	/**
	 * Set of annotation_items. They are ordered, and unique. 
	 */
	//private HashSet<AnnotationItem> annotationItems = new HashSet<AnnotationItem>();
	private TreeSet<AnnotationItem> annotationItems = new TreeSet<AnnotationItem>();
	
	/**
	 * Map of annotation_items linking an annotation_item to an Offset.
	 */
	private HashMap<AnnotationItem, Integer> annotationItemsToOffsets = new HashMap<AnnotationItem, Integer>();
	
	/**
	 * Set of annotation_set_items. They are ordered, and unique.
	 */
	//private HashSet<AnnotationSetItem> annotationSetItems = new HashSet<AnnotationSetItem>();
	private TreeSet<AnnotationSetItem> annotationSetItems = new TreeSet<AnnotationSetItem>();
	
	/**
	 * Map of annotation_set_items linking an annotation_set_item to an Offset.
	 */
	private HashMap<AnnotationSetItem, Integer> annotationSetItemsToOffsets = new HashMap<AnnotationSetItem, Integer>();
	
	/**
	 * Set of annotation_set_ref_list. They are not ordered, but we need them to be unique.
	 */
	private HashSet<AnnotationSetRefList> annotationSetRefLists = new HashSet<AnnotationSetRefList>();
	
	/**
	 * Map of annotation_set_ref_lists linking an annotation_set_ref_list to an Offset.
	 */
	private HashMap<AnnotationSetRefList, Integer> annotationSetRefListsToOffsets = new HashMap<AnnotationSetRefList, Integer>();
	
	/**
	 * Map linking a Class to the offset of its annotation_directory_item, if any.
	 */
	//private HashMap<ClassDefinitionItem, Integer> classesToAnnotationDirectoryItemOffsets = new HashMap<ClassDefinitionItem, Integer>();
	private HashMap<AnnotationDirectoryItem, Integer> classesToAnnotationDirectoryItemOffsets = new HashMap<AnnotationDirectoryItem, Integer>();
	
	/**
	 * Map linking a ClassName (fully qualified) to its Class Definition Item. It is useful to sort the
	 * Class at the end of the parsing.
	 */
	// We use a sorted map instead of a regular map so that names of classes are roughly ordered although some
	// items may be introduced to respect the type hierarchy (required constraint imposed by sortClasses().
	// This rough ordering is the one used by dx. [PC - 7/1/13]
	private Map<String, ClassDefinitionItem> classNameToClassDefinitionItem = new TreeMap<String, ClassDefinitionItem>();

	/**
	 * HashSet of annotation_directory_items. They are unique, but we don't care about their order.
	 */
	private HashSet<AnnotationDirectoryItem> annotationDirectoryItems = new HashSet<AnnotationDirectoryItem>();
	
	
	
	
	// ---------------------------------------
	// Getters and Setters.
	// ---------------------------------------
	
	/**
	 * Returns the number of Strings currently parsed.
	 * @return the number of Strings currently parsed.
	 */
	public int getStringCount() {
		return strings.size();
	}
	
	/**
	 * Returns the number of Types currently parsed.
	 * @return the number of Types currently parsed.
	 */
	public int getTypeCount() {
		return types.size();
	}
	
	/**
	 * Returns the number of Prototypes currently parsed.
	 * @return the number of Prototypes currently parsed.
	 */
	public int getPrototypeCount() {
		return prototypes.size();
	}
	
	/**
	 * Returns the number of Fields currently parsed.
	 * @return the number of Fields currently parsed.
	 */
	public int getFieldCount() {
		return fields.size();
	}
	
	/**
	 * Returns the number of Methods currently parsed.
	 * @return the number of Methods currently parsed.
	 */
	public int getMethodCount() {
		return methods.size();
	}
	
	/**
	 * Returns the number of Classes currently parsed.
	 * @return the number of Classes currently parsed.
	 */
	public int getClassDefinitionCount() {
		return classNameToClassDefinitionItem.size();
	}
	
	/**
	 * Returns the number of TypeList currently parsed.
	 * @return the number of TypeList currently parsed.
	 */
	public int getTypeListCount() {
		return typeList.size();
	}
	
	/**
	 * Returns the ordered Strings of the Application. It is only set when the Application has been fully parsed.
	 * @return the ordered Strings of the Application.
	 */
	public TreeSet<String> getStrings() {
		return strings;
	}
	
	/**
	 * Returns the ordered Types of the Application. It is only set when the Application has been fully parsed.
	 * @return the ordered Types of the Application.
	 */
	public TreeSet<String> getTypes() {
		return types;
	}
	
	/**
	 * Returns the ordered Fields of the Application. It is only set when the Application has been fully parsed.
	 * @return the ordered Fields of the Application.
	 */
	public TreeSet<Field> getFields() {
		return orderedFields;
	}
	
	/**
	 * Returns the ordered Prototypes of the Application. It is only set when the Application has been fully parsed.
	 * @return the ordered Prototypes of the Application.
	 */
	public TreeSet<Prototype> getPrototypes() {
		return orderedPrototypes;
	}
	
	/**
	 * Returns the ordered Methods of the Application. It is only set when the Application has been fully parsed.
	 * @return the ordered Methods of the Application.
	 */
	public TreeSet<Method> getMethods() {
		return orderedMethods;
	}
	
	/**
	 * Returns the ordered TypeLists of the Application. It is only set when the Application has been fully parsed.
	 * @return the ordered TypeLists of the Application.
	 */
	public TreeSet<TypeList> getTypeList() {
		return orderedTypeList;
	}
	
	/**
	 * Returns the index of a String. The Symbolic index is used as long as the application hasn't been
	 * fully parsed.
	 * @param string the String we want the index of. 
	 * @return the index of the String.
	 */
	public int getStringIndex(String string) {
		if (useSymbolicElements) {
			return symbolicStringsToIndexes.get(string);
		} else {
			return resolvedStringsToIndexes.get(string);
		}
	}
	
	/**
	 * Returns the Offset of a TypeList. <i>The Dex file must have been fully parsed before.</i>
	 * @param typeList the TypeList to find.
	 * @return the offset of the TypeList, from the beginning of the file.
	 */
	public int getTypeListOffset(TypeList typeList) {
		return typeListsToOffsets.get(typeList);
	}
	
	/**
	 * Sets an Offset to a TypeList.
	 * @param typeList the TypeList to give the Offset.
	 * @param offset the Offset to set.
	 */
	public void setTypeListOffset(TypeList typeList, int offset) {
		typeListsToOffsets.put(typeList, offset);
	}
	
	/**
	 * Returns the Index of a Type. The Symbolic index is used as long as the application hasn't been
	 * fully parsed.
	 * @param type name of the Type.
	 * @return the index of the Type.
	 */
	public int getTypeIndex(String type) {
		if (useSymbolicElements) {
			return symbolicTypesToIndexes.get(type);
		} else {
			return resolvedTypesToIndexes.get(type);
		}
	}
	
	/**
	 * Returns the Index of a Prototype. <i>The Dex file must have been fully parsed before.</i>
	 * @param prototype the Prototype.
	 * @return the index of the Prototype.
	 */
	public int getPrototypeIndex(Prototype prototype) {
		return prototypesToIndexes.get(prototype);
	}
	
	/**
	 * Returns the Index of a Field. The Symbolic index is used as long as the application hasn't been
	 * fully parsed.
	 * @param field the Field.
	 * @return the index of the Field.
	 */
	public int getFieldIndex(Field field) {
		if (useSymbolicElements) {
			return symbolicFieldsToIndexes.get(field);
		} else {
			return resolvedFieldsToIndexes.get(field);
		}
	}
	
	/**
	 * Returns the Index of a Method. The Symbolic index is used as long as the application hasn't been
	 * fully parsed.
	 * @param method the Method.
	 * @return the index of the Method.
	 */
	public int getMethodIndex(Method method) {
		if (useSymbolicElements) {
			return symbolicMethodsToIndexes.get(method);
		} else {
			return resolvedMethodsToIndexes.get(method);
		}
	}
	
	/**
	 * Returns the offset of an annotation_item.
	 * @param annotationItem the offset of the annotation_item.
	 * @return the offset of the annotation_item.
	 */
	public int getAnnotationItemOffset(AnnotationItem annotationItem) {
		return annotationItemsToOffsets.get(annotationItem);
	}
	
	/**
	 * Sets the offset of an annotation_item.
	 * @param annotationItem the annotation_item.
	 * @param offset the Offset of the annotation_item.
	 */
	public void setAnnotationItemOffset(AnnotationItem annotationItem, int offset) {
		annotationItemsToOffsets.put(annotationItem, offset);
	}

	/**
	 * Returns the offset of an annotation_set_item.
	 * @param annotationSetItem the annotation_set_item.
	 * @return the offset of the annotation_set_item.
	 */
	public int getAnnotationSetItemOffset(AnnotationSetItem annotationSetItem) {
		return annotationSetItemsToOffsets.get(annotationSetItem);
	}
	
	/**
	 * Sets the offset of an annotation_set_item.
	 * @param annotationSetItem the annotation_set_item.
	 * @param offset the offset of the annotation_set_item.
	 */
	public void setAnnotationSetItemOffset(AnnotationSetItem annotationSetItem, int offset) {
		annotationSetItemsToOffsets.put(annotationSetItem, offset);
	}
	
	/**
	 * Returns the offset of an annotation_set_ref_list.
	 * @param annotationSetRefList the annotation_set_ref_list.
	 * @return offset the offset of the annotation_set_ref_list.
	 */
	public int getAnnotationSetRefListOffset(AnnotationSetRefList annotationSetRefList) {
		return annotationSetRefListsToOffsets.get(annotationSetRefList);
	}
	
	/**
	 * Sets the offset of an annotation_set_ref_list.
	 * @param annotationSetRefList the annotation_set_ref_list.
	 * @param offset the offset of the annotation_set_ref_list.
	 */
	public void setAnnotationSetRefListOffset(AnnotationSetRefList annotationSetRefList, int offset) {
		annotationSetRefListsToOffsets.put(annotationSetRefList, offset);
	}

	/**
	 * Returns the offset of an annotation_directory_item, or 0 if the Class doesn't have an
	 * annotation_directory_item.
	 * @param annotationDirectoryItem the annotation_directory_item.
	 * @return the offset of the annotation_directory_item, or 0.
	 */
	public int getAnnotationDirectoryItemOffset(AnnotationDirectoryItem annotationDirectoryItem) {
		if (classesToAnnotationDirectoryItemOffsets.containsKey(annotationDirectoryItem)) {
			return classesToAnnotationDirectoryItemOffsets.get(annotationDirectoryItem);
		} else {
			return 0;
		}
	}
	
	/**
	 * Sets the offset of an annotation_directory_item.
	 * @param annotationDirectoryItem the annotation_directory_item belongs to.
	 * @param offset the offset of the annotation_directory_item.
	 */
	public void setAnnotationDirectoryItemOffset(AnnotationDirectoryItem annotationDirectoryItem, int offset) {
		classesToAnnotationDirectoryItemOffsets.put(annotationDirectoryItem, offset);
	}
	
	/**
	 * Returns the Set of annotation_items.
	 * @return the Set of annotation_items.
	 */
	public TreeSet<AnnotationItem> getAnnotationItems() {
		return annotationItems;
	}
	
	/**
	 * Returns the set of annotation_set_item.
	 * @return the set of annotation_set_item.
	 */
	public TreeSet<AnnotationSetItem> getAnnotationSetItems() {
		return annotationSetItems;
	}
	
	/**
	 * Returns the set of annotation_set_ref_list.
	 * @return the set of annotation_set_ref_list.
	 */
	public HashSet<AnnotationSetRefList> getAnnotationSetRefLists() {
		return annotationSetRefLists;
	}
	
	/**
	 * Returns the count of annotation_items.
	 * @return the count of annotation_items.
	 */
	public int getAnnotationItemCount() {
		return annotationItems.size();
	}
	
	/**
	 * Returns the count of annotation_set_items.
	 * @return the count of annotation_set_items.
	 */
	public int getAnnotationSetItemCount() {
		return annotationSetItems.size();
	}
	
	/**
	 * Returns the count of annotation_set_ref_lists.
	 * @return the count of annotation_set_ref_lists.
	 */
	public int getAnnotationSetRefListsCount() {
		return annotationSetRefLists.size();
	}
	
	/**
	 * Returns the count of the annotation_directory_item.
	 * @return the count of the annotation_directory_item.
	 */
	public int getAnnotationDirectoryItemCount() {
		return classesToAnnotationDirectoryItemOffsets.size();
	}
	
	/**
	 * Returns the offset of the encoded_array_item of a static_values_offset for one Class if it exists.
	 * @param cdi the Class.
	 * @return the offset of the encoded_array_item of a static_values_offset for one Class, or 0 if no
	 * 		   offset has been found.
	 */
	public int getOffsetOfStaticValuesEncodedArrayItemOfClass(ClassDefinitionItem cdi) {
		int result = 0;
		if (staticValuesEncodedArrayItemOffsets.containsKey(cdi)) {
			result = staticValuesEncodedArrayItemOffsets.get(cdi);
		}
		return result;
	}
	
	/**
	 * Returns an ordered list of the classes of the Application. It is only completed when the
	 * Application has been fully parsed.
	 * @return an ordered list of the classes of the Application.
	 */
	public ArrayList<ClassDefinitionItem> getClasses() {
		return orderedClasses;
	}
	
	/**
	 * Returns the annotation_directory_items, unique but non-ordered.
	 * @return the annotation_directory_items.
	 */
	public HashSet<AnnotationDirectoryItem> getAnnotationDirectoryItems() {
		return annotationDirectoryItems;
	}
	
	/**
	 * Returns the resolved method index from the symbolic index. The application must have been
	 * fully parsed for the conversion to be possible.
	 * @param methodIndex the symbolic method index.
	 * @return the resolved method index.
	 */
	public int getResolvedMethodIndexFromSymbolicMethodIndex(int methodIndex) {
		return symbolicMethodsIndexToResolvedMethodsIndex[methodIndex];
	}
	
	/**
	 * Returns the resolved Field index from the symbolic index. The application must have been
	 * fully parsed for the conversion to be possible.
	 * @param fieldIndex the symbolic Field index.
	 * @return the resolved Field index.
	 */
	public int getResolvedFieldIndexFromSymbolicFieldIndex(int fieldIndex) {
		return symbolicFieldsIndexToResolvedFieldsIndex[fieldIndex];
	}
	
	/**
	 * Returns the resolved Type index from the symbolic index. The application must have been
	 * fully parsed for the conversion to be possible.
	 * @param typeIndex the symbolic Type index.
	 * @return the resolved Type index.
	 */
	public int getResolvedTypeIndexFromSymbolicTypeIndex(int typeIndex) {
		return symbolicTypesIndexToResolvedTypesIndex[typeIndex];
	}
	
	/**
	 * Returns the resolved String index from the symbolic index. The application must have been
	 * fully parsed for the conversion to be possible.
	 * @param stringIndex the symbolic String index.
	 * @return the resolved String index.
	 */
	public int getResolvedStringIndexFromSymbolicStringIndex(int stringIndex) {
		return symbolicStringsIndexToResolvedStringsIndex[stringIndex];
	}
	
	
	// ---------------------------------------
	// Public methods.
	// ---------------------------------------
	
	/**
	 * Adds a String to the Constant Pool. Nothing happens if the String already exists or is null.
	 * Empty strings are authorized.
	 * @param string the String to add.
	 */
	public void addStringToConstantPool(String string) {
		if (string != null) {
			if (useSymbolicElements) {
				if (!symbolicStringsToIndexes.containsKey(string)) {
					symbolicStringsToIndexes.put(string, symbolicStringsToIndexes.size());
				}
			} 
			strings.add(string);
		}
	}
	
	/**
	 * Adds Strings to the Constant pool. Nothing happens if the String already exists or is null.
	 * Empty strings are authorized.
	 * @param strings the Strings to add.
	 */
	public void addStringsToConstantPool(String[] strings) {
		if (strings != null) {
			for (String string : strings) {
				addStringToConstantPool(string);
			}
		}
	}
	
	/**
	 * Adds a Type to the Constant Pool. Nothing happens if the Type already exists, is null
	 * or empty. The Type is added to both the Types and Strings sets.
	 * @param type the Type to add.
	 */
	public void addTypeToConstantPool(String type) {
		if ((type != null) && (!"".equals(type))) {
			addStringToConstantPool(type);
			if (useSymbolicElements) {
				if (!symbolicTypesToIndexes.containsKey(type)) {
					symbolicTypesToIndexes.put(type, symbolicTypesToIndexes.size());
				}
			} 
			types.add(type);
		}
	}
	
	/**
	 * Adds Types to the Constant Pool. Nothing happens if the Types already exists, are null
	 * or empty. The Type is added to both the Types and Strings sets.
	 * @param types the Types to add.
	 */
	public void addTypesToConstantPool(String[] types) {
		if (types != null) {
			for (String type : types) {
				addTypeToConstantPool(type);
			}
		}
	}
	
	/**
	 * Adds a Prototype to the Constant Pool. Adds the Strings inside, but also the Prototype to the
	 * Prototype Pool.
	 * @param prototype Prototype to add.
	 */
	public void addPrototypeToConstantPool(Prototype prototype) {
		Prototype existing = prototypes.get(prototype);
		if (existing == null) {
			prototype.initialize();
			addStringToConstantPool(prototype.getShortyDescriptor());
			addTypeToConstantPool(prototype.getReturnType());
			addTypeListToConstantPool(prototype.getParameterTypes());
			prototypes.put(prototype,prototype);
		}
	}
	
	/**
	 * Creates a Prototype from the descriptor and adds it to the Constant Pool, as well as the
	 * Strings inside. Returns the Prototype created.
	 * @param descriptor full descriptor in the TypeDescriptor format.
	 * @return the Prototype created.
	 */
	public Prototype addPrototypeToConstantPool(String descriptor) {
		Prototype prototype = new Prototype(descriptor);
		Prototype existing = prototypes.get(prototype);
		if (existing == null) {
			addPrototypeToConstantPool(prototype);
		} else {
			prototype = existing;
		}
		return prototype;
	}

	
	/**
	 * Adds a TypeList to the Constant Pool, as well as the Strings inside. If the TypeList is empty, it is
	 * not added.
	 * @param typeList the TypeList to add to the Constant Pool.
	 */
	public void addTypeListToConstantPool(TypeList typeList) {
		if (typeList.size() > 0) {
			for (String type : typeList.getTypeList()) {
				addTypeToConstantPool(type);
			}
			this.typeList.add(typeList);
		}
	}
	
	/**
	 * Adds a Method to the Constant Pool being built. Does nothing if the Constant Pool already
	 * contains a similar item <i>unless the stored Method was tagged Unknown</i>, in which case it means 
	 * that it was just referred by an instruction, but not actually visited, and so lacks information.
	 * This Method can be called when declaring a Method without knowing what is inside, for example
	 * when a reference to this Method is found. In that case, the information are added.
	 * The method name, descriptor, and class name are added to the constant pool if necessary.
	 * In return is received the Method related to the given parameters.
     * 
	 * @param methodName the method name.
	 * @param className the class name of the method.
	 * @param methodDescriptor the method descriptor.
	 * @param access the access flags of the method.
	 * @param signature the signature of the method. May be Null.
	 * @param exceptionNames the exceptions of the method. May be null.
	 * @return the Method related to the given parameters.
	 */
	public Method addMethodToConstantPool(String methodName, String className,
			String methodDescriptor, int access, String[] signature, String[] exceptionNames) {
		
		// int prototypeHashCode = Prototype.calculateHashCode(methodDescriptor);
		// int methodHashCode = Method.calculateHashCode(methodName, className, prototypeHashCode);
		Prototype prototype = addPrototypeToConstantPool(methodDescriptor);
		Method method = new Method(methodName, className, prototype);
		Method foundMethod = methods.get(method);
		if (foundMethod == null) {
			method.init(access, signature, exceptionNames, this);
			methods.put(method,method);
			symbolicMethodsToIndexes.put(method, symbolicMethodsToIndexes.size());
				
			// Add the name, descriptor, className and exceptions to the Constant Pool.
			addStringToConstantPool(methodName);
			addTypeToConstantPool(className);
			addStringsToConstantPool(signature);
			addTypesToConstantPool(exceptionNames);
			return method;
		} else {
			// The Method to add has already been added before, probably when it has been declared.
			// Maybe the current instance is the declaration. One of the two has incomplete information.
			// We must give them these information without replacing the object, because Instructions are
			// referring the incomplete Method.

			// If the current method is unknown, it is useless to get information from it.
			if (((access & Opcodes.ACC_UNKNOWN) == 0) && foundMethod.isUnknown()) {
				foundMethod.completeInformation(access, exceptionNames, signature, this);
			}
			return foundMethod; // In the end, we return the previously added Method.
		}
		
		
	}
	
	/**
	 * Adds a Field in the Constant Pool. Does nothing if the constant pool already
	 * contains a similar item. The field name, type, and class owning name are added to the constant
	 * pool. In return is received the Field related to the given parameters.
	 * This method can be called when declaring a Field without knowing what is inside, for example
	 * when a reference to this Field is found. In that case, the Field access should be ACC_UNKNOWN. 
	 * @param fieldName name of the Field
	 * @param desc type of the field in TypeDescriptor format.
	 * @param classOwningName name of the class owning the Field.
	 * @param access the access flags of the Field.
	 * @param signature the Signature of the field. May be Null.
	 * @param value the value of the Field. Null for non-final static Fields.
	 * @return the Field related to the given parameters.
	 */
	public Field addFieldToConstantPool(String fieldName, String desc, String classOwningName,
			int access, String[] signature, Object value) {

		// int fieldHashCode = Field.calculateHashCode(fieldName, desc, classOwningName);
		Field field = new Field(fieldName, desc, classOwningName);
		Field foundField = fields.get(field);
		if (foundField == null) {
			field.init(access, signature, value, this);
			fields.put(field,field);
			
			symbolicFieldsToIndexes.put(field, symbolicFieldsToIndexes.size());
			
			// Adds the name, type and class owning name to the constant pool.
			addStringToConstantPool(fieldName);
			addTypeToConstantPool(desc);
			addTypeToConstantPool(classOwningName);
			addStringsToConstantPool(signature);
			return field;
		} else {
			// The Field to add has already been added before, probably when it has been declared.
			// Or maybe the current instance is the declaration. One of the two has incomplete information.
			// We must give them these information without replacing the object, because Instructions are
			// referring the incomplete Field.
			
			// If the current Field is unknown, it is useless to get information from it.
			if (((access & Opcodes.ACC_UNKNOWN) == 0) && foundField.isUnknown()) {
				foundField.completeInformation(access, signature, value, this);
			}
			
			return foundField;  // In the end, we use the already added Field.
		}
	}
	
	/**
	 * Adds a class to the constant pool. Nothing happens if the Class already exists.
	 * The class name, superClass name, signature and interfaces name are added to the constant pool.
     * In return is received the ClassDefinitionItem related to the class.
	 * @param className the name of the class.
	 * @param superName the name of the super class.
	 * @param access the access flags of the class.
	 * @param interfaceNames the name of the interfaces of the class.
	 * @param signature the signature of the class. Used only for generics. May be null.
	 * @return the ClassDefinitionItem related to the class.
	 */
	public ClassDefinitionItem addClassToConstantPool(String className, String superName,
			int access, String[] interfaceNames, String[] signature) {
		
		// We build a Class and add it to the Class constant pool.
		if (!classNameToClassDefinitionItem.containsKey(className)) {
			TypeList interfaceTypeList = new TypeList(interfaceNames);
			ClassDefinitionItem cdi = new ClassDefinitionItem(className, superName,
					access, interfaceTypeList, signature, this);

			classNameToClassDefinitionItem.put(className, cdi);
			
			// Adds the name, superName, signature and interfaces to the constant pool.
			addTypeToConstantPool(className);
			addTypeToConstantPool(superName);
			addStringsToConstantPool(signature);
			
			addTypeListToConstantPool(interfaceTypeList);
			return cdi;
		} else {
			return classNameToClassDefinitionItem.get(className);
		}

	}
	
	/**
	 * Adds an annotation_item to the Constant Pool. It must have its full content known.
	 * @param annotationItem the annotation_item to add.
	 */
	public void addAnnotationItemToConstantPool(AnnotationItem annotationItem) {
		annotationItems.add(annotationItem);
	}
	
	/**
	 * Adds an annotation_set_item to the Constant Pool. It must have its full content known.
	 * Doesn't add the annotation_set_item if it doesn't contain any annotation_item.
	 * @param annotationSetItem the annotation_set_item to add.
	 */
	public void addAnnotationSetItemToConstantPool(AnnotationSetItem annotationSetItem) {
		if (annotationSetItem.getNbAnnotationItems() > 0) {
			annotationSetItems.add(annotationSetItem);
		}
	}
	
	/**
	 * Adds an annotation_set_item to the Constant Pool. It must have its full content known.
	 * Even annotation_set_item with a size of 0 are added. This is especially useful for the
	 * Annotated Parameter, as annotation_set_ref_list will probably contain empty annotation_set_items.
	 * @param annotationSetItem the annotation_set_item to add.
	 */
	public void addAnnotationSetItemNoSizeCheck(AnnotationSetItem annotationSetItem) {
		annotationSetItems.add(annotationSetItem);
	}
	
	/**
	 * Adds an annotation_set_ref_list to the Constant Pool. It also registers all the annotation_set_items
	 * it contains to the Constant Pool. It must have its full content known.
	 * @param annotationSetRefList the annotation_set_ref_list to add.
	 */
	public void addAnnotationSetRefListToConstantPool(AnnotationSetRefList annotationSetRefList) {
		if (annotationSetRefList.getNbAnnotationSetItemsUsed() > 0) {
			annotationSetRefLists.add(annotationSetRefList);
			
			// Registers all the annotation_set_items it contains.
			for (AnnotationSetItem annotationSetItem : annotationSetRefList.getAnnotationSetItems()) {
				addAnnotationSetItemNoSizeCheck(annotationSetItem);
			}
		}
	}
	
	/**
	 * Adds an offset for the encoded_array_item of a static_values_offset for one Class.
	 * @param cdi the class.
	 * @param offset the offset to add.
	 */
	public void addOffsetForStaticValuesEncodedArrayItemOfClass(ClassDefinitionItem cdi, int offset) {
		staticValuesEncodedArrayItemOffsets.put(cdi, offset);
	}

	/**
	 * Adds an annotation_directory_item to the Constant Pool.
	 * @param annotationDirectoryItem the annotation_directory_item to add.
	 */
	public void addAnnotationDirectoryItem(AnnotationDirectoryItem annotationDirectoryItem) {
		annotationDirectoryItems.add(annotationDirectoryItem);
	}

	/**
	 * Prepares the various elements to be generated. This must be done only once, after the whole
	 * Application has been parsed. It consists in generating the sorted collections, and building the
	 * Index Maps.
	 */
	public void prepareGeneration() {
		generateSortedCollections();
		buildIndexMaps();
	}
	
	/**
	 * Generates the sorted collections from the unsorted ones (it is faster to sort them once the
	 * Application is parsed than adding elements one after one).
	 */
	private void generateSortedCollections() {
		orderedTypeList = new TreeSet<TypeList>(typeList);
		orderedFields = new TreeSet<Field>(fields.values());
		orderedPrototypes = new TreeSet<Prototype>(prototypes.values());
		orderedMethods = new TreeSet<Method>(methods.values());
		sortClasses();
	}

	/**
	 * Builds the Index Maps, allowing to link Strings, Types, Fields... to an Index, sorted in the order
	 * requested by the Dex format.
	 * Also creates the link between the symbolic elements and the resolved elements.
	 * The application must have been fully parsed, and the sorted collections must have been generated
	 * (generateSortedCollections method).
	 */
	private void buildIndexMaps() {
		useSymbolicElements = false;
		int symbolicIndex;
		
		// Builds the Strings index map, and links the symbolic Strings indexes to the resolved Strings indexes.
		resolvedStringsToIndexes.clear();
		symbolicStringsIndexToResolvedStringsIndex = new int[strings.size()];
		int index = 0;
		for (String string : strings) {
			resolvedStringsToIndexes.put(string, index);
			
			symbolicIndex = symbolicStringsToIndexes.get(string);
			symbolicStringsIndexToResolvedStringsIndex[symbolicIndex] = index;
			
			index++;
		}
		
		// Builds the Types index map, and links the symbolic Types indexes to the resolved Types indexes.
		resolvedTypesToIndexes.clear();
		symbolicTypesIndexToResolvedTypesIndex = new int[types.size()];
		index = 0;
		for (String type : types) {
			resolvedTypesToIndexes.put(type, index);
			
			symbolicIndex = symbolicTypesToIndexes.get(type);
			symbolicTypesIndexToResolvedTypesIndex[symbolicIndex] = index;
			
			index++;
		}
		
		// Builds the Prototypes index map. The collection must be sorted first.
		prototypesToIndexes.clear();
		index = 0;
		for (Prototype prototype : orderedPrototypes) {
			prototypesToIndexes.put(prototype, index);
			index++;
		}
		
		// Builds the Fields index map, and links the symbolic Fields indexes to the resolved Types indexes.
		resolvedFieldsToIndexes.clear();
		symbolicFieldsIndexToResolvedFieldsIndex = new int[fields.size()];
		index = 0;
		for (Field field : orderedFields) {
			resolvedFieldsToIndexes.put(field, index);
			
			symbolicIndex = symbolicFieldsToIndexes.get(field);
			symbolicFieldsIndexToResolvedFieldsIndex[symbolicIndex] = index;
			
			index++;
		}
		
		// Builds the Methods index map, and links the symbolic Methods indexes to the resolved Types indexes.
		// The collection must be sorted first.
		resolvedMethodsToIndexes.clear();
		symbolicMethodsIndexToResolvedMethodsIndex = new int[orderedMethods.size()];
		index = 0;
		for (Method method : orderedMethods) {
			resolvedMethodsToIndexes.put(method, index);
			
			symbolicIndex = symbolicMethodsToIndexes.get(method);
			symbolicMethodsIndexToResolvedMethodsIndex[symbolicIndex] = index;
			
			index++;
		}
	}
	

	/**
	 * Sorts the Classes into a new List. Each Class must be written after its possible Parent.
	 */
	private void sortClasses() {
		orderedClasses = new ArrayList<ClassDefinitionItem>();
		
		Collection<ClassDefinitionItem> classes = classNameToClassDefinitionItem.values();
		for (ClassDefinitionItem cdi : classes) {
			sortClassRec(cdi);
		}
		
		if (classNameToClassDefinitionItem.size() != orderedClasses.size()) {
			try { throw new Exception("Sorted Classes list doesn't match the original list !");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	private void sortClassRec(ClassDefinitionItem cdi) {
		// We make sure the SuperClass hasn't already been encoded.
		if (!orderedClasses.contains(cdi)) {
			String superClass = cdi.getSuperClassName();
			// Checks the super class, if any.
			if (superClass != null) {
				sortClassRecString(superClass);
			}
			// Checks the interfaces, if any.
			TypeList tl = cdi.getInterfaces();
			for (String className : tl.getTypeList()) {
				sortClassRecString(className);
			}
			
			orderedClasses.add(cdi);
		}
	}
	
	private void sortClassRecString(String className) {
		ClassDefinitionItem ncdi = classNameToClassDefinitionItem.get(className);
		// The SuperClass may not be found if it's a Java SuperClass like Object, Enum etc.
		// We also make sure the SuperClass hasn't already been encoded.
		if ((ncdi != null) && (!orderedClasses.contains(ncdi))) {
			sortClassRec(ncdi);
		}
	}

	

	
}
